/*
 * =====================================================================================
 *       Filename:  http.c
 * =====================================================================================
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <netdb.h>
#include "../zipmap.h"
#include "../dict.h"
#include "http.h"

static void parseMUQ(const char* line, char* method, int method_size, char* uri, int uri_size, char* query_string)
{
    int i, j;
    int size = method_size - 1;
    j = 0;
    for (i = 0; line[i] != ' ' && j < size; i++) {
        method[j++] = line[i];
    }
    method[j] = '\0';

    size = uri_size - 1;
    j = 0;
    for (i++; line[i] != ' ' && j < size; i++) {
        uri[j++] = line[i];
    }
    uri[j] = '\0';

    query_string = uri;
    while ((*query_string) != '?' && (*query_string != '\0'))
        query_string++;

    if (*query_string == '?') {
        *query_string = '\0';
        query_string++;
    }

    if (strstr(uri, "..")) {
        sprintf(uri, "/");
    }
}

static void parseKV(const char* line, char* key, int key_size, char* value, int value_size)
{
    const char* p = line;
    int i = 0;
    while (1) {
        key[i] = *p;
        if (*p == ':') {
            key[i] = '\0';
            break;
        }
        if ((*p == '\n') || (*p == '\0') || (i >= key_size - 1)) {
            key[0] = '\0';
            value[0] = '\0';
            return;
        }
        p++;
        i++;
    }
    
    while((*p == ':') || (*p == ' '))
        p++;

    i = 0;
    while(1) {
        value[i] = *p;
        if ((*p == '\n') || (*p == '\0') || (i >= value_size - 1)) {
            value[i] = '\0';
            break;
        }
        p++;
        i++;
    }
}

static void discardLeft(clientRequestTask *t) {
    int n = 1;
    while (n > 0) {
        n = read(t->fd, t->line, sizeof(t->line));
    }
}

static void httpHeader(clientRequestTask *t, const char* key, const char* value)
{
    sprintf(t->line, "%s: %s\r\n", key, value);
    if (t->lwritten > 0) {
        t->lwritten = write(t->fd, t->line, strlen(t->line));
    }
}

static void httpStatus(clientRequestTask *t, int status, const char* message)
{
    sprintf(t->line, "HTTP/1.1 %d %s\r\n", status, message);
    if (t->lwritten > 0) {
        t->lwritten = write(t->fd, t->line, strlen(t->line));
    }
}

static void httpBody(clientRequestTask *t, const char* body) {
    if (t->lwritten > 0) {
        t->lwritten = write(t->fd, body, strlen(body));
    }
}

static void httpUnimplemented(clientRequestTask *t)
{
    httpStatus(t, 501, "Method Not Implemented");
    httpHeader(t, "Content-Type", "text/html");
    httpBody(t, "\r\n");
    const char* html = "<HTML><HEAD><TITLE>Method Not Implemented</TITLE></HEAD><BODY><P>HTTP request method not supported.</BODY></HTML>";
    httpBody(t, html);
}

static void httpNotFound(clientRequestTask *t)
{
    httpStatus(t, 404, "Not Found");
    httpHeader(t, "Content-Type", "text/html");
    httpHeader(t, "Connection", "close");
    httpBody(t, "\r\n");
    const char* html = "<HTML><HEAD><TITLE>Not Found</TITLE></HEAD><BODY><P>The page not found!</BODY></HTML>";
    httpBody(t, html);
}

static void httpNotAcceptable(clientRequestTask *t)
{
    httpStatus(t, 406, "Not Acceptable");
    httpHeader(t, "Content-Type", "text/html");
    httpHeader(t, "Connection", "close");
    httpBody(t, "\r\n");
    const char* html = "<HTML><HEAD><TITLE>Not Acceptable</TITLE></HEAD><BODY><P>Project wrong!</BODY></HTML>";
    httpBody(t, html);
}

static void httpUnauthorized(clientRequestTask *t)
{
    httpStatus(t, 401, "Unauthorized");
    httpHeader(t, "Content-Type", "text/html");
    httpHeader(t, "Connection", "close");
    httpBody(t, "\r\n");
    const char* html = "<HTML><HEAD><TITLE>Unauthorized</TITLE></HEAD><BODY><P>Authorization required!</BODY></HTML>";
    httpBody(t, html);
}

static int getLine(clientRequestTask *t)
{
    int i = 0;
    char c = '\0';
    int fd = t->fd;

    int n, size = sizeof(t->line);
    while ((i < size - 1) && (c != '\n'))
    {
        n = recv(fd, &c, 1, 0);
        if (-1 == n && errno == EAGAIN && t->exit == 0){
            sem_wait(&(t->sem));
            continue;
        }
        else if (n > 0)
        {
            if (c == '\r')
            {
                n = recv(fd, &c, 1, MSG_PEEK);
                if ((n > 0) && (c == '\n'))
                    recv(fd, &c, 1, 0);
                else
                    c = '\n';
            }
            t->line[i] = c;
            i++;
        }
        else
            c = '\n';
    }
    t->line[i] = '\0';

    return i;
}

static int saveFile(clientRequestTask *t)
{
    FILE *resource = NULL;

    resource = fopen(t->path, "w");
    if (resource == NULL)
        return -1;

    int save_fd = fileno(resource);
    int n = 0, result;
    char c;
    while (n < t->contentLength) {
        result = recv(t->fd, t->line, 1024, 0);
        if (result == 0)
            break;
        else if (result == -1) {
            if (errno == EAGAIN && t->exit == 0) {
                sem_wait(&(t->sem));
                continue;
            }
            break;
        }
        else
            n += write(save_fd, t->line, result);
    }
    fclose(resource);
    return n;
}

#include "verbs.c"
#include "handle.c"

void cmdInit()
{
    int num = sizeof(_httpCommandTable) / sizeof(httpCommand);
    int i;
    _commands = dictCreate(&dictTypeStringKey);
    for (i = 0; i < num; i++) {
        dictSet(_commands, _httpCommandTable[i].name, _httpCommandTable + i);
    }
}

void cmdFree()
{
    dictFree(_commands);
}

